package dst.ass3.jms.scheduler.impl;

import java.io.Serializable;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;

import org.apache.openejb.api.LocalClient;

import dst.ass3.dto.TaskWrapperDTO;
import dst.ass3.jms.Constants;
import dst.ass3.jms.scheduler.IScheduler;
import dst.ass3.jms.scheduler.IScheduler.ISchedulerListener.InfoType;


@LocalClient
public class Scheduler implements IScheduler {

	private final static Logger LOGGER = Logger.getLogger(Scheduler.class.getName());
	
	private ISchedulerListener listener;
    private Connection connection;
    private Session session;
    private MessageProducer messageProducer;
    private MessageConsumer messageConsumer;
    
    @Resource(name = Constants.CONNECTION_FACTORY)
    private ConnectionFactory connectionFactory;
    
    @Resource(name = Constants.SERVER_QUEUE)
    private Queue serverQ;
    
    @Resource(name = Constants.SCHEDULER_QUEUE)
    private Queue schedulerQ;
    
    /**
     * Lookup all the Resources
     */
	@Override
	public void start() {
		try {
            connection = connectionFactory.createConnection();
            connection.setClientID(Scheduler.class.getName());
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            
            messageProducer = session.createProducer(serverQ);  //write to Server-Queue
             
            messageConsumer = session.createConsumer(schedulerQ);  //read from Scheduler-Queue
            
            messageConsumer.setMessageListener(new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    Scheduler.this.onMessage(message);
                }
            });

            connection.start();
        } catch (JMSException e) {
            e.printStackTrace();
        } 
	}

	
	/**
	 * receives the messages that are in the Scheduler-Queue (so the came from the Server) 
	 * because that is the only communication connection
	 */
	protected void onMessage(Message message) {
		if (message == null) {
			LOGGER.info("Message received is null");
			return;
		}
		else{
			ObjectMessage m = (ObjectMessage) message;
			
			try {
				int receivedType = m.getIntProperty(Constants.PROPERTY_MESSAGE_TYPE);
				
				LOGGER.info("Message type: "+receivedType+" and object "+ m.getObject());
				
				switch (receivedType) {
					case Constants.MSG_SERVER_TO_SCHEDULER_CREATED:
						serverCreated(m.getObject());
						break;
					case Constants.MSG_SERVER_TO_SCHEDULER_INFO:
						serverInfo(m.getObject());
						break;
					case Constants.MSG_SERVER_TO_SCHEDULER_DENIED:
						serverDenied(m.getObject());
						break;
					case Constants.MSG_SERVER_TO_SCHEDULER_PROCESSED:
						serverProcessed(m.getObject());
						break;
					default:
						LOGGER.info("Unknown Type received: "+receivedType);
				}
			} catch (JMSException e) {
				e.printStackTrace();
			}
		}
	}

    private void serverProcessed(Serializable object) {
    	if (object == null) {
    		LOGGER.info("No object received");
            return;
        }
    	else if (listener == null) {
    		LOGGER.info("Listener is null");
            return;
        }
    	else{
    		listener.notify(InfoType.PROCESSED, ((TaskWrapperDTO)object));
    	}
    }

    private void serverDenied(Serializable object) {
    	if (object == null) {
    		LOGGER.info("No object received");
            return;
        }
    	else if (listener == null) {
    		LOGGER.info("Listener is null");
            return;
        }
    	else{
    		listener.notify(InfoType.DENIED, ((TaskWrapperDTO)object));
    	}
    }

    private void serverInfo(Serializable object) {
    	if (object == null) {
    		LOGGER.info("No object received");
            return;
        }
    	else if (listener == null) {
    		LOGGER.info("Listener is null");
            return;
        }
    	else{
    		listener.notify(InfoType.INFO, ((TaskWrapperDTO)object));
    	}
    }

    private void serverCreated(Serializable object) {
    	if (object == null) {
    		LOGGER.info("No object received");
            return;
        }
    	else if (listener == null) {
    		LOGGER.info("Listener is null");
            return;
        }
    	else{
    		listener.notify(InfoType.CREATED, ((TaskWrapperDTO)object));
    	}
    }
	
	
	@Override
    public void stop() {
		try {
			if (messageConsumer != null)
				messageConsumer.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
		try {
			if (messageProducer != null)
				messageProducer.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
		try {
			if (session != null)
				session.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
		try {
			if (connection != null)
				connection.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
	 }

	/**
	 * advises the server to create a new Task
	 */
	@Override
    public void assign(long taskId) {
        TaskWrapperDTO dto = new TaskWrapperDTO();
        dto.setTaskId(taskId);

        try {
            ObjectMessage msg = session.createObjectMessage(dto);
            msg.setIntProperty(Constants.PROPERTY_MESSAGE_TYPE, Constants.MSG_SCHEDULER_TO_SERVER_CREATE);
            
            messageProducer.send(msg);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

	/**
	 * Advises the Server to send information about a task identified by a ID
	 */
	@Override
	public void info(long taskWrapperId) {
		TaskWrapperDTO dto = new TaskWrapperDTO();
		dto.setId(taskWrapperId);

		try {
			ObjectMessage msg = session.createObjectMessage(dto);
			msg.setIntProperty(Constants.PROPERTY_MESSAGE_TYPE, Constants.MSG_SCHEDULER_TO_SERVER_INFO);
			
			messageProducer.send(msg);
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}

	/**
	 * sets the listener
	 */
	@Override
	public void setSchedulerListener(ISchedulerListener listener) {
		this.listener = listener;		
	}

  

}